package org.jiuling.unifyinfo.spring.exception.handing;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.jiuling.basics.converter.StringConvert;
import org.jiuling.basics.exception.BasicsException;
import org.jiuling.basics.exception.BasicsRuntimeException;
import org.jiuling.basics.response.ResponseData;
import org.jiuling.basics.response.enums.ResponseCodeEnum;
import org.jiuling.basics.validation.BooleanVerify;
import org.jiuling.unifyinfo.spring.exception.handing.config.ExceptionHandingConfig;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.Set;
import java.util.stream.Collectors;

/**
 * 统一异常处理
 * @author 紫灵
 * @date 2022年10月09日 15时13分08秒
 */
@RestControllerAdvice
public class UnifiedExceptionHandling {

    /** 异常处理配置 */
    private final ExceptionHandingConfig exceptionHandingConfig;

    public UnifiedExceptionHandling(ExceptionHandingConfig exceptionHandingConfig) {
        this.exceptionHandingConfig = exceptionHandingConfig;
    }

    @ResponseBody
    @ExceptionHandler(BindException.class)
    public ResponseData<?> bindExceptionIntercept(BindException e){
        return new ResponseData<>(
                ResponseCodeEnum.ERROR,
                removeExclamationMark(
                        e.getBindingResult()
                                .getAllErrors()
                                .parallelStream()
                                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                                .collect(
                                        Collectors
                                                .joining("、")
                                )
                )+"！"
        );
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseData<?> methodArgumentNotValidExceptionIntercept(MethodArgumentNotValidException e){
        return new ResponseData<>(
                ResponseCodeEnum.ERROR,
                removeExclamationMark(
                        e.getBindingResult()
                                .getAllErrors()
                                .parallelStream()
                                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                                .distinct()
                                .collect(
                                        Collectors
                                                .joining("、")
                                )
                )+"！"

        );
    }

    @ResponseBody
    @ExceptionHandler(NoResourceFoundException.class)
    public ResponseData<?> noResourceFoundException(NoResourceFoundException e){
        if (BooleanVerify.isTrue(exceptionHandingConfig.getDebug())) e.printStackTrace();
        return new ResponseData<>(ResponseCodeEnum.ERROR,"该请求地址不存在！");
    }

    /**
     * 基础异常拦截
     * @param e 本地工具异常
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(BasicsException.class)
    public ResponseData<?> basicsExceptionIntercept(BasicsException e){
        return new ResponseData<>(ResponseCodeEnum.ERROR,e.getLocalizedMessage());
    }

    /**
     * 基础运行时异常拦截
     * @param e 基础运行时异常
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(BasicsRuntimeException.class)
    public ResponseData<?> basicsRuntimeExceptionIntercept(BasicsRuntimeException e){
        return new ResponseData<>(ResponseCodeEnum.ERROR,e.getLocalizedMessage());
    }

    /**
     * Http请求方法不支持异常拦截
     * @param e Http请求方法不支持异常
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseData<?> httpRequestMethodNotSupportedExceptionIntercept(HttpRequestMethodNotSupportedException e){
        return new ResponseData<>(ResponseCodeEnum.METHODS_ERROR, StringConvert.format("该接口不支持 {} 请使用 {} 方法","{}",e.getMethod(),e.getSupportedHttpMethods()));
    }

    /**
     * 约束违反例外拦截
     * @param e 约束违反例外
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseData<?> constraintViolationExceptionIntercept(ConstraintViolationException e){
        StringBuilder stringBuilder = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolationSet = e.getConstraintViolations();
        constraintViolationSet.forEach(constraintViolation -> {
            stringBuilder.append(constraintViolation.getMessage());
            stringBuilder.append("、");
        });
        stringBuilder.deleteCharAt(stringBuilder.length()-1);
        return new ResponseData<>(
                ResponseCodeEnum.ERROR,
                removeExclamationMark(stringBuilder.toString()
                        .replaceAll("！", "")
                        .replaceAll("!",""))
                +"！"
        );
    }



    @ResponseBody
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseData<?> httpMessageNotReadableExceptionIntercept(HttpMessageNotReadableException e){
        return new ResponseData<>(ResponseCodeEnum.EXCEPTION,e.getLocalizedMessage());
    }

    /**
     * 异常拦截
     * @param e 异常
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseData<?> exceptionIntercept(Exception e){
        if (BooleanVerify.isTrue(exceptionHandingConfig.getDebug())) e.printStackTrace();
        return new ResponseData<>(ResponseCodeEnum.EXCEPTION,"异常操作！");
    }

    /**
     * 运行时异常拦截
     * @param e 运行时异常
     * @return 异常处理结果
     */
    @ResponseBody
    @ExceptionHandler(RuntimeException.class)
    public ResponseData<?> runtimeExceptionIntercept(RuntimeException e){
        if (BooleanVerify.isTrue(exceptionHandingConfig.getDebug())) e.printStackTrace();
        return new ResponseData<>(ResponseCodeEnum.EXCEPTION,"异常操作！");
    }

    /**
     * 移除感叹号
     * @param msg 消息
     * @return 处理后的结果
     */
    private String removeExclamationMark(String msg){
        return msg.replaceAll("！","").replaceAll("!","");
    }

}
